package com.cellulam.auth.filters;

import com.cellulam.auth.annotations.Anonymous;
import com.cellulam.framework.core.auth.UserBasicInfo;
import com.cellulam.framework.core.auth.UserClient;
import com.cellulam.framework.core.context.AppContext;
import com.cellulam.framework.core.exceptions.BusinessException;
import com.cellulam.framework.core.exceptions.ForbidAnonymousException;
import com.cellulam.framework.core.exceptions.UnauthorizedException;
import com.cellulam.framework.core.utils.EmptyUtils;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 权限验证
 */
@Component
@Slf4j
public class LoginAuthFilter extends HandlerInterceptorAdapter {
    private static final ThreadLocal<Map<String, UserBasicInfo>> USER_LOCAL_CONTEXT = ThreadLocal.withInitial(() -> Maps.newConcurrentMap());

    public static final String HEADER_ACCESS_TOKEN = "access-token";

    @Autowired
    private UserClient userClient;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getMethod().equals(HttpMethod.OPTIONS.name())) {
            return true;
        }

        String accessToken = request.getHeader(HEADER_ACCESS_TOKEN);
        boolean login = setUser(accessToken, request);

        if (login ||
                (handler instanceof HandlerMethod
                        && ((HandlerMethod) handler).hasMethodAnnotation(Anonymous.class))) {
            return true;
        }

        String handlerName = handler.getClass().getName();
        log.debug("handler: {}, token: {}, method: {}", handlerName,
                accessToken,
                request.getMethod());

        if (StringUtils.isEmpty(accessToken)) {
            throw new ForbidAnonymousException();
        } else {
            throw new UnauthorizedException("token无效: " + accessToken);
        }
    }

    private boolean setUser(String accessToken, HttpServletRequest request) {
        Map<String, UserBasicInfo> localContext = USER_LOCAL_CONTEXT.get();

        if (StringUtils.isEmpty(accessToken)) {
            return false;
        }

        UserBasicInfo userBasicInfo = this.getUser(accessToken);
        if (userBasicInfo == null) {
            return false;
        }
        localContext.put(HEADER_ACCESS_TOKEN, userBasicInfo);
        request.setAttribute(UserClient.USER_ATTRIBUTE_NAME, userBasicInfo);

        if (EmptyUtils.isNotEmpty(localContext)) {
            USER_LOCAL_CONTEXT.set(localContext);
        }
        AppContext.setUserId(userBasicInfo.getUserId());
        return true;
    }

    private UserBasicInfo getUser(String accessToken) {
        try {
            return userClient.getUserBasicInfo(accessToken);
        } catch (BusinessException e) {
            log.info("获取用户信息失败, token: {}, code: {}, msg: {}",
                    accessToken,
                    e.getCode(),
                    e.getMessage());
        } catch (Exception e) {
            log.error("token: {}", accessToken, e);
        }
        return null;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        USER_LOCAL_CONTEXT.get().clear();
        super.afterCompletion(request, response, handler, ex);
    }

    public static UserBasicInfo getContext(String contextName) {
        return USER_LOCAL_CONTEXT.get().get(contextName);
    }

    public static UserBasicInfo setContext(String key, UserBasicInfo value) {
        return USER_LOCAL_CONTEXT.get().put(key, value);
    }
}
